package view;

import java.util.ArrayList;
import java.util.HashMap;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;

/**
 * This class represents the game's Board.<br>
 * Holds the game's {@link Cell}s array.<br>
 * extends {@link Canvas}
 * default Ctor gets:
 * @param rows for board rows
 * @param cols for board columns
 * @param hashCell for CellValue:{@link Image} mapping
 * @see Canvas
 * @see HashMap
 * @see Image
 * @author Barak & Tom
 */
public class Board extends Canvas {
	public Cell[][] newBoardData;
	int rows;
	int cols;
	int score;

	public Board(int rows, int cols, Composite parent, int style, String cell,
			HashMap<Integer, Image> hashCell) {
		super(parent, style);
		this.rows = rows;
		this.cols = cols;
		setLayout(new GridLayout(rows, true));

		newBoardData = new Cell[rows][cols];
		for (int i = 0; i < newBoardData.length; i++) {
			for (int j = 0; j < newBoardData[i].length; j++) {
				newBoardData[i][j] = new Cell(this, SWT.BORDER, hashCell);
				newBoardData[i][j].setLayoutData(new GridData(SWT.FILL,
						SWT.FILL, true, true));
				// boardData[i][j] = 0;
				newBoardData[i][j].setValue(0);
			}
		}
		score = 0;
	}

	public int GetScore() {
		return score;
	}

	public int getBoardRows() {
		return rows;
	}

	public int getBoardColumns() {
		return cols;
	}

	public ArrayList<Cell> getAvailableCells() {
		ArrayList<Cell> emptyCells = new ArrayList<Cell>();
		for (int i = 0; i < newBoardData.length; i++) {
			for (int j = 0; j < newBoardData[i].length; j++) {
				if (newBoardData[i][j].getValue() == 0) {
					emptyCells.add(newBoardData[i][j]);
				}
			}
		}
		return emptyCells;
	}

	public double smoothness() {
		double smoothness = 0;
		for (int x = 0; x < 4; x++) {
			for (int y = 0; y < 4; y++) {
				if (this.cellOccupied(x, y)) {
					double value = Math.log(newBoardData[x][y].getValue())
							/ Math.log(2);
					// right, down
					int[] targetCell = findFarthestPosition(x, y, "Right");

					if (this.cellOccupied(targetCell[0], targetCell[1])) {
						Cell target = newBoardData[targetCell[0]][targetCell[1]];
						double targetValue = Math.log(target.getValue())
								/ Math.log(2);
						smoothness -= Math.abs(value - targetValue);
					}
				}
			}
		}
		return smoothness;
	}

	private int[] findFarthestPosition(int x, int y, String direction) {
		int[] targetPosition = new int[2];
		int i = 0, j = 0;
		switch (direction.toLowerCase()) {
		case "up":
			i = 0;
			j = -1;
			break;
		case "down":
			i = 0;
			j = 1;
			break;
		case "left":
			i = -1;
			j = 0;
			break;
		case "right":
			i = 1;
			j = 0;
			break;
		default:
			break;
		}
		while (newBoardData[x + i][y + j] != null) {
			targetPosition[0] = x;
			targetPosition[1] = y;
			x++;
			y++;
		}
		return targetPosition;
	}

	private boolean cellOccupied(int x, int y) {
		return newBoardData[x][y].getValue() != 0;
	}

	public double monotonicity() {
		 // scores for all four directions
		  double[] totals = new double[]{0,0,0,0};

		  // up/down direction
		  for (int x=0; x<4; x++) {
			  int current = 0;
			  int next = current+1;
		    while ( next<4 ) {
		      while ( next<4 && !this.cellOccupied(x, next)) {
		        next++;
		      }
		      if (next>=4) { next--; }
		      double currentValue = this.cellOccupied(x, current) ? Math.log(newBoardData[x][current].getValue()) / Math.log(2) : 0;
		      double nextValue = this.cellOccupied(x, next) ? Math.log(newBoardData[x][next].getValue()) / Math.log(2) : 0;
		      if (currentValue > nextValue) {
		        totals[0] += nextValue - currentValue;
		      }
		      else if (nextValue > currentValue) {
		        totals[1] += currentValue - nextValue;
		      }
		      current = next;
		      next++;
		    }
		  }

		  // left/right direction
		  for (int y=0; y<4; y++) {
			  int current = 0;
			  int next = current+1;
		    while ( next<4 ) {
		      while ( next<4 && !this.cellOccupied(next, y)) {
		        next++;
		      }
		      if (next>=4) { next--; }
		      double currentValue = cellOccupied(current, y) ?  Math.log(newBoardData[current][y].getValue()) / Math.log(2) : 0;
		      double nextValue = cellOccupied(next, y) ?  Math.log(newBoardData[next][y].getValue()) / Math.log(2) : 0;
		      if (currentValue > nextValue) {
		        totals[2] += nextValue - currentValue;
		      } else if (nextValue > currentValue) {
		        totals[3] += currentValue - nextValue;
		      }
		      current = next;
		      next++;
		    }
		  }

		  return Math.max(totals[0], totals[1]) + Math.max(totals[2], totals[3]);
	}

	public double maxValue() {
		 int max = 0;
		  for (int x=0; x<4; x++) {
		    for (int y=0; y<4; y++) {
		      if (cellOccupied(x, y)) {
		    	  int value = newBoardData[x][y].getValue();
		        if (value > max) {
		          max = value;
		        }
		      }
		    }
		  }

		  return Math.log(max) / Math.log(2);
	}
}
